package com.maciek.integracja_aplikacji.ws;

import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.handler.MessageContext;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.maciek.integracja_aplikacji.bo.PersonBo;
import com.maciek.integracja_aplikacji.model.Database;
import com.maciek.integracja_aplikacji.model.ErrorCode;
import com.maciek.integracja_aplikacji.model.Person;
import com.maciek.integracja_aplikacji.model.PersonalFile;
 
@WebService
public class PersonalFileManager {
 
	private static final Logger log = LoggerFactory.getLogger(PersonalFileManager.class);
	
    @Resource
    WebServiceContext wsctx;

    private static String DATABASE_FILEPATH = "D:/workspace/WebServices/target/integracja_aplikacji-1.0.0-BUILD-SNAPSHOT/WEB-INF/classes/database.xml";
    private static String USERS_FILEPATH = "users.xml";
    private static Document DOC_DATABASE = null;
    private static Document DOC_USERS = null;
	private static XPath XPATH = null;
	
	PersonBo personBo;
	
	@WebMethod(exclude = true)
	public void setPersonBo(PersonBo personBo) {
		this.personBo = personBo;
	}
	
	public PersonalFileManager() {
		initXML();
	}
	
	@WebMethod
	public Boolean checkAuthorization() {
		log.info("checkAuthorization");
		return getAutorization(wsctx.getMessageContext());
	}
	
	@WebMethod
	public Person findPerson(String firstName, String lastName, String pesel, Database type) {
		log.info("findPerson");
		
		if(!getAutorization(wsctx.getMessageContext())) {
			return null;
		}
		
		Person result = null;
		
		if(type == Database.XML) {
			result = findPersonXML(firstName, lastName, pesel);
		}
		else if(type == Database.Hibernate){
			result = findPersonHibernate(firstName, lastName, pesel);
		}
		
		return result;
	}
	
	@WebMethod
	public ErrorCode removePerson(Person person, Database type) {
		log.info("removePerson");
		
		if(!getAutorization(wsctx.getMessageContext())) {
			return ErrorCode.FAILURE;
		}
		
		if(type == Database.XML) {
			return removePersonXML(person);
		} else if(type == Database.Hibernate) {
			return removePersonHibernate(person);
		} else {
			return ErrorCode.FAILURE;
		}
	}

	@WebMethod
	public ErrorCode addPerson(Person person, Database type) {
		log.info("addPerson");
		
		if(!getAutorization(wsctx.getMessageContext())) {
			return ErrorCode.FAILURE;
		}
		
		if(type == Database.XML) {
			return addPersonXML(person);
		} else if(type == Database.Hibernate) {
			return addPersonHibernate(person);
		} else {
			return ErrorCode.FAILURE;
		}
	}
	
	@WebMethod
	public ErrorCode addPersonalFile(Person person, Database type) {
		log.info("addPersonalFile");
		
		if(!getAutorization(wsctx.getMessageContext())) {
			return ErrorCode.FAILURE;
		}
		
		if(type == Database.XML) {
			return ErrorCode.FALSE;
		} else if(type == Database.Hibernate) {
			return addPersonalFileHibernate(person);
		} else {
			return ErrorCode.FAILURE;
		}
		
	}
	
	/*************************************************************************************/
	
	private Person findPersonHibernate(String firstName, String lastName, String pesel) {
		
		if ((firstName == null || "".equals(firstName))
				&& (lastName == null || "".equals(lastName))
				&& (pesel == null || "".equals(pesel))) {
			return null;
		}
		
		return personBo.findBy(firstName, lastName, pesel);
	}
	
	private ErrorCode removePersonHibernate(Person person) {
		if (person.getPesel() == null || "".equals(person.getPesel())) {
			return ErrorCode.FALSE;
		}
		
		return personBo.delete(person);
	}
	
	private ErrorCode addPersonHibernate(Person person) {
		for(PersonalFile pf : person.getPersonalFiles()) {
			pf.setPerson(person);
		}
		return personBo.persist(person);
	}
	
	private ErrorCode addPersonalFileHibernate(Person person) {
		for(PersonalFile pf : person.getPersonalFiles()) {
			pf.setPerson(person);
		}
		return personBo.persist(person);
	}
	
	
	/*************************************************************************************/
	
	
	private static Person findPersonXML(String firstName, String lastName, String pesel) {
		
		if ((firstName == null || "".equals(firstName))
				&& (lastName == null || "".equals(lastName))
				&& (pesel == null || "".equals(pesel))) {
			return null;
		}
		
		Person result = null;
		String expression = "/People/Person[";
		boolean flag = false;
		
		if(!"".equals(firstName) && firstName != null) {
			expression += flag ? " and " : "";
			expression += "firstname='" + firstName + "'";
			flag = true;
		}
		if(!"".equals(lastName) && lastName != null) {
			expression += flag ? " and " : "";
			expression += "lastname='" + lastName + "'";
			flag = true;
		}
		if(!"".equals(pesel) && pesel != null) {
			expression += flag ? " and " : "";
			expression += "@PESEL='" + pesel + "'";
			flag = true;
		}
		expression += "]";
		
		try {
			Node node = (Node) XPATH.compile(expression).evaluate(DOC_DATABASE, XPathConstants.NODE);
			if(node != null) {
				
				result = new Person();
				NodeList nodeList = node.getChildNodes();
				
				for(int i=0; i<nodeList.getLength(); i++) {
					
					if("firstname".equals(nodeList.item(i).getNodeName())) {
						result.setFirstName(nodeList.item(i).getTextContent());
					} else if("lastname".equals(nodeList.item(i).getNodeName())) {
						result.setLastName(nodeList.item(i).getTextContent());
					} else if("address".equals(nodeList.item(i).getNodeName())) {
						result.setAddress(nodeList.item(i).getTextContent());
					} else if("zip".equals(nodeList.item(i).getNodeName())) {
						result.setZipCode(nodeList.item(i).getTextContent());
					} else if("place".equals(nodeList.item(i).getNodeName())) {
						result.setPlace(nodeList.item(i).getTextContent());
					}
				}
				
				result.setPesel(((Element) node).getAttribute("PESEL"));
				result.setPlaceOfBirth("");
				result.setCountry("");
				result.setIsCriminalRecord(false);
				result.setPersonalFiles(new HashSet<PersonalFile>());
			}
		} catch (XPathExpressionException e) {
			e.printStackTrace();
		}
		
		return result;
	}
	
	private static ErrorCode removePersonXML(Person person) {
		
		if (person.getPesel() == null || "".equals(person.getPesel())) {
			return ErrorCode.FALSE;
		}
		
		try {
			String expression = "/People/Person[@PESEL='" + person.getPesel() + "']";
			Node node = (Node) XPATH.compile(expression).evaluate(DOC_DATABASE, XPathConstants.NODE);
			
			if(node != null) {
				DOC_DATABASE.getFirstChild().removeChild(node);
				return persistXML();
			}
			else {
				return ErrorCode.FALSE;
			}
			
		} catch (XPathExpressionException e) {
			e.printStackTrace();
			return ErrorCode.FAILURE;
		}
	}
	
	private static ErrorCode addPersonXML(Person person) {
		Element firstname = DOC_DATABASE.createElement("firstname");
		firstname.setTextContent(person.getFirstName());
		
		Element lastname = DOC_DATABASE.createElement("lastname");
		lastname.setTextContent(person.getLastName());
		
		Element address = DOC_DATABASE.createElement("address");
		address.setTextContent(person.getAddress());
		
		Element zip = DOC_DATABASE.createElement("zip");
		zip.setTextContent(person.getZipCode());
		
		Element place = DOC_DATABASE.createElement("place");
		place.setTextContent(person.getPlace());
		
		Element personElement = DOC_DATABASE.createElement("Person");
		personElement.setAttribute("PESEL", person.getPesel());
		personElement.appendChild(firstname);
		personElement.appendChild(lastname);
		personElement.appendChild(address);
		personElement.appendChild(zip);
		personElement.appendChild(place);
		DOC_DATABASE.getFirstChild().appendChild(personElement);
		
		return persistXML();
	}
	
	/***************************************************************************************/
	
	private static boolean getAutorization(MessageContext mctx) {
				 
		Map http_headers = (Map) mctx.get(MessageContext.HTTP_REQUEST_HEADERS);
        List userList = (List) http_headers.get("Username");
        List passList = (List) http_headers.get("Password");
 
        String username = "";
        String password = "";
 
        if(userList != null){
        	username = userList.get(0).toString();
        }
 
        if(passList != null){
        	password = passList.get(0).toString();
        }
		
        try {
        	Node node = (Node) XPATH.compile("/Users/User[@id='" + username + "']").evaluate(DOC_USERS, XPathConstants.NODE);
        	
        	if(node != null) {
        		String password2 = node.getTextContent();
        		
        		if(password2.equals(password)) {
        			return true;
        		}
        		else {
        			return false;
        		}
    		}
        	else {
        		return false;
        	}
		} catch (XPathExpressionException e) {
			e.printStackTrace();
			return false;
		}
	}
	
	private void initXML() {
		try {
			DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder docBuilder;
			docBuilder = docFactory.newDocumentBuilder();
			DOC_DATABASE = docBuilder.parse(DATABASE_FILEPATH);
			DOC_USERS = docBuilder.parse(this.getClass().getClassLoader().getResource(USERS_FILEPATH).getPath());
			
			XPathFactory xPathfactory = XPathFactory.newInstance();
			XPATH = xPathfactory.newXPath();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private static ErrorCode persistXML() {
		try {
			TransformerFactory transformerFactory = TransformerFactory.newInstance();
			Transformer transformer = transformerFactory.newTransformer(); 
			DOMSource source = new DOMSource(DOC_DATABASE);
			StreamResult result = new StreamResult(new File(DATABASE_FILEPATH));
			transformer.transform(source, result);
			return ErrorCode.TRUE;
		} catch (TransformerConfigurationException e) {
			e.printStackTrace();
			return ErrorCode.FAILURE;
		} catch (TransformerException e) {
			e.printStackTrace();
			return ErrorCode.FAILURE;
		}
	}


 
}